/*
  Copyright (c) 2001-2009 Broadway Technology, LLC. All rights reserved.

  This source code file is only for the use of an Authorized User as expressly
  granted by license from Broadway Technology, LLC ("Broadway") and is to be
  treated as confidential information not to be disclosed to any third party
  without prior written consent from Broadway. Permission to reproduce this
  source code file and to prepare derivative works therefrom shall be as
  expressly set forth from the license between Broadway and Authorized User,
  provided the above copyright statement and this notice are included with all
  reproductions and derivative works.
*/


/* Simple (but powerful) TCP Performance Tester
 * ============================================
 * Application can be invoked as server or client. It sends or receive data to/from
 * the server in burst.
 *
 * Data exchange:
 *    1. Client connect to server
 *    2. CLIENT -----> SERVER: client send to server some running parameter 
 *    3. CLIENT <----- SERVER: server send buffers to client (optional)
 *    4. CLIENT -----> SERVER: client send buffers to server (optional)
 *    5. CLIENT -----> SERVER: client send an command to tell the server to
 *                             loop (1) or stop (0) to server
 */


// --------------------------------------------------------------------------
// Cross-platform code
// --------------------------------------------------------------------------
/* {{{ Cross-platform includes
 * --------------------------------------------------------------------------
 */
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")

#else

// POSIX
#include <errno.h>
#include <strings.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ctype.h>
#include <fcntl.h>
#define SOCKET        int
#define SOCKET_ERROR  -1
#define INVALID_SOCKET  -1
#endif
/* }}} */
/* {{{ Cross-platform Macros
 * --------------------------------------------------------------------------
 */
#ifdef _WIN32
#define tcpperfSnprintf          _snprintf
#define tcpperfStrcasecmp        lstrcmpiA
#define tcpperfGetLastError()    WSAGetLastError()
#define tcpperfSleep(sec)        Sleep(sec*1000)
#define tcpperfCloseSocket       closesocket
#define tcpperfZeroMemory        ZeroMemory
#else
#define tcpperfSnprintf          snprintf
#define tcpperfStrcasecmp        strcasecmp
#define tcpperfGetLastError()    errno
#define tcpperfSleep             sleep
#define tcpperfCloseSocket       close
#define tcpperfZeroMemory        bzero
#endif

#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS    0
#endif

#ifndef EXIT_FAILURE
#define EXIT_FAILURE    1
#endif
/* }}} */
/* {{{ Support for gettimeofday on Windows
 * --------------------------------------------------------------------------
 */
#ifdef _WIN32
// Windows doesn't have gettimeofday().
// The following code was taken from:
// http://suacommunity.com/dictionary/gettimeofday-entry.php#id385556

#pragma warning(disable : 4996)     // Disable warning use of deprecated variables (like _timezone)

#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
  #define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
#else
  #define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL
#endif
 
struct timezone
{
  int  tz_minuteswest; /* minutes W of Greenwich */
  int  tz_dsttime;     /* type of dst correction */
};

static int gettimeofday(struct timeval *tv, struct timezone *tz)
{
  // Define a structure to receive the current Windows filetime
  FILETIME ft;
 
  // Initialize the present time to 0 and the timezone to UTC
  unsigned __int64 tmpres = 0;
  static int tzflag = 0;
 
  if (NULL != tv)
  {
    GetSystemTimeAsFileTime(&ft);
 
    // The GetSystemTimeAsFileTime returns the number of 100 nanosecond 
    // intervals since Jan 1, 1601 in a structure. Copy the high bits to 
    // the 64 bit tmpres, shift it left by 32 then or in the low 32 bits.
    tmpres |= ft.dwHighDateTime;
    tmpres <<= 32;
    tmpres |= ft.dwLowDateTime;
 
    // Convert to microseconds by dividing by 10
    tmpres /= 10;
 
    // The Unix epoch starts on Jan 1 1970.  Need to subtract the difference 
    // in seconds from Jan 1 1601.
    tmpres -= DELTA_EPOCH_IN_MICROSECS;
 
    // Finally change microseconds to seconds and place in the seconds value. 
    // The modulus picks up the microseconds.
    tv->tv_sec = (long)(tmpres / 1000000UL);
    tv->tv_usec = (long)(tmpres % 1000000UL);
  }
 
  if (NULL != tz)
  {
    if (!tzflag)
    {
      _tzset();
      tzflag++;
    }
  
    // Adjust for the timezone west of Greenwich
    tz->tz_minuteswest = _timezone / 60;
    tz->tz_dsttime = _daylight;
  }
 
  return 0;
}
#endif
/* }}} */


// --------------------------------------------------------------------------
// Application Defaults
// --------------------------------------------------------------------------
#define APPLICATION_VERSION_STRING  "1.0"
#define APPLICATION_NAME            "tcpperf"
#define DEFAULT_PORT_NUMBER         21500
#define DEFAULT_SERVER_ADDRESS      "localhost"
#define DEFAULT_BIND_ADDRESS        NULL
#define DEFAULT_BLOCK_SIZE          8192
#define DEFAULT_BLOCK_SEND          0
#define DEFAULT_BLOCK_RECV          1
#define DEFAULT_LOOP_COUNT          1
#define DEFAULT_LOOP_DELAY          1


// --------------------------------------------------------------------------
// Argument data type
// --------------------------------------------------------------------------
/* {{{ ServerArgs
 * --------------------------------------------------------------------------
 */
struct ServerArgs {
  unsigned short port;
  const char *bindAddress;
};
#define DEFAULT_SERVER_ARGS   { DEFAULT_PORT_NUMBER,  \
                                DEFAULT_BIND_ADDRESS }
/* }}} */
/* {{{ ClientArgs
 * --------------------------------------------------------------------------
 */
struct ClientArgs {
  unsigned short port;
  const char *serverAddress;
  int sendCount;
  int recvCount;
  int blockSize;
  int loopCount;
  int loopDelay;
};
#define DEFAULT_CLIENT_ARGS   { DEFAULT_PORT_NUMBER, \
                                DEFAULT_SERVER_ADDRESS, \
                                DEFAULT_BLOCK_SEND,  \
                                DEFAULT_BLOCK_RECV,  \
                                DEFAULT_BLOCK_SIZE,  \
                                DEFAULT_LOOP_COUNT,  \
                                DEFAULT_LOOP_DELAY }
/* }}} */
/* {{{ CommonArgs
 * --------------------------------------------------------------------------
 */
struct CommonArgs {
  int recvBufSize;
  int sendBufSize;
  int noNagle;
};
#define DEFAULT_COMMON_ARGS   { 0, 0, 0 }
/* }}} */


// --------------------------------------------------------------------------
// Globals
// --------------------------------------------------------------------------
static int isVerbose = 1;
static int theDefaultSendBufferSize = 0;
static int theDefaultRecvBufferSize = 0;
static int theDefaultNagle = 0;
/* {{{ initGlobals
 * --------------------------------------------------------------------------
 * Initializes the network and global variables
 * \return 1 if success, 0 if an error occurred
 */
static int initGlobals() {
  SOCKET sock = -1;
  int paramLong;
  socklen_t optLen = sizeof(paramLong);
  int i;

#ifdef _WIN32
  // Initialize Winsock
  {
    WSADATA wsaData;
    i = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (i != 0) {
        printf("WSAStartup failed: %d\n", i);
        return EXIT_FAILURE;
    }
  }
#endif
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock == SOCKET_ERROR) {
    fprintf(stderr, "Error creating default socket: %d\n", tcpperfGetLastError());
    return 0;
  }
  if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&paramLong, &optLen) == SOCKET_ERROR) {
    fprintf(stderr, "getsockopt(SO_RCVBUF) failed: %d\n", tcpperfGetLastError());
    return 0;
  }
  theDefaultRecvBufferSize = (int)paramLong;
  if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&paramLong, &optLen) == SOCKET_ERROR) {
    fprintf(stderr, "getsockopt(SO_SNDBUF) failed: %d\n", tcpperfGetLastError());
    return 0;
  }
  theDefaultSendBufferSize = (int)paramLong;

  optLen = sizeof(i);
  if (getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&i, &optLen) == SOCKET_ERROR) {
    fprintf(stderr, "getsockopt(TCP_NODELAY) failed: %d\n", tcpperfGetLastError());
    return 0;
  }
  theDefaultNagle = !(int)i;
  
  tcpperfCloseSocket(sock);
  return 1;
}
/* }}} */
/* {{{ shutdownGlobals
 * --------------------------------------------------------------------------
 * Performs a clean shutown of global resources
 */
static void shutdownGlobals() {
#ifdef _WIN32
  WSACleanup();
#endif
}
/* }}} */

// --------------------------------------------------------------------------
// Functions
// --------------------------------------------------------------------------
/* {{{ printResults
 * --------------------------------------------------------------------------
 * Prints the statistics calculated during the execution. Used by the server
 * side and client side.
 *
 * \param loopCount the loop number
 * \param blockSize the block size used in the test
 * \param blockSend number of blocks sent. 0=no data was sent
 * \param blockRecv number of blocks received. 0=no data was received
 * \param tWriteStart absolute time when the write phase started
 * \param tWriteEnd absolute time when the write phase completed
 * \param tReadStart absolute time when the read phase started
 * \param tReadEnd absolute time when the read phase completed
 */
static void printResults(int loopCount,
                         int blockSize,
                         int blockSend,
                         int blockRecv,
                         struct timeval *tWriteStart,
                         struct timeval *tWriteEnd,
                         struct timeval *tReadStart,
                         struct timeval *tReadEnd) {
  double tSec;
  double tUsec;
  double total;
  double kBps;

  printf("SUMMARY RESULTS FOR LOOP # %d\n", loopCount);
  if (blockSend) {
    tSec = (tWriteEnd->tv_sec - tWriteStart->tv_sec);
    tUsec = (tWriteEnd->tv_usec - tWriteStart->tv_usec);
    total = tSec * 1000000 + tUsec;
    kBps = ((double)blockSize * (double)blockSend) / (total/1000000);

    printf("    Total write time ....: %f uSec\n", total);
    printf("    Total bytes sent ....: %d\n", blockSize * blockSend);
    printf("    Avg throughput ......: %f Kb/sec\n", kBps);
  }
  if (blockRecv) {
    tSec = (tReadEnd->tv_sec - tReadStart->tv_sec);
    tUsec = (tReadEnd->tv_usec - tReadStart->tv_usec);
    total = (tSec * 1000000 + tUsec) ;
    kBps = ((double)blockSize * (double)blockRecv) / (total/1000000);
    printf("    Total read time .....: %f uSec\n", total);
    printf("    Total bytes received : %d\n", blockSize * blockRecv);
    printf("    Avg throughput ......: %f Kb/sec\n", kBps);
  }
}
/* }}} */
/* {{{ setSockOptionVerify
 * --------------------------------------------------------------------------
 * A convenience function to modify a socket option and read it back to ensure
 * the change was accepted.
 *
 * \param sock the socket
 * \param level socket level
 * \param socketOption the socket option parameter
 * \param value the new integer value
 * \return 1 if success, 0 if an error occurred
 */
static int setSockOptionVerify(SOCKET sock, int level, int socketOption, int bufferSize) {
  int param = bufferSize;
  socklen_t optLen = sizeof(param);
  if (setsockopt(sock, level, socketOption, (char *)&param, optLen) == SOCKET_ERROR) {
    fprintf(stderr, "setsockopt failed: %d\n", tcpperfGetLastError());
    return 0;
  }

  // Read it back
  param = 0;
  if (getsockopt(sock, level, socketOption, (char *)&param, &optLen) == SOCKET_ERROR) {
    fprintf(stderr, "getsockopt failed: %d\n", tcpperfGetLastError());
    return 0;
  }

  if (param != bufferSize) {
    fprintf(stderr, "socket option verification failed: read=%d, expected=%d\n", param, bufferSize);
    return 0;
  }
  return 1;
}
/* }}} */
/* {{{ setSocketBuffer
 * --------------------------------------------------------------------------
 * Modify the send and receive socket buffer size. This function does also
 * verification of the change (reads it back to make sure the value was
 * accepted).
 * The change is performed only if the provided values are different than the
 * default oness.
 *
 * \param sock the socket
 * \param sendBufferSize the new value of the send buffer size
 * \param recvBufferSize the new value of the recv buffer size
 * \return 1 if success, 0 if an error occurred
 */
static int setSocketBufferSize(SOCKET sock, int sendBufferSize, int recvBufferSize) {
  if (sendBufferSize != theDefaultSendBufferSize) {
    if (!setSockOptionVerify(sock, SOL_SOCKET, SO_SNDBUF, sendBufferSize)) {
      return 0;
    }
  }

  if (recvBufferSize != theDefaultRecvBufferSize) {
    if (!setSockOptionVerify(sock, SOL_SOCKET, SO_RCVBUF, recvBufferSize)) {
      return 0;
    }
  }
  return 1; 
}
/* }}} */
/* {{{ disableNagle
 * --------------------------------------------------------------------------
 * Disable the Nagle algorithm (TCP_NODELAY)
 *
 * \param sock the socket
 * \return 1 if success, 0 if an error occurred
 */
static int disableNagle(SOCKET sock) {
  if (!setSockOptionVerify(sock, IPPROTO_TCP, TCP_NODELAY, 1)) {
    return 0;
  }
  return 1;
}
/* }}} */
/* {{{ runServer
 * --------------------------------------------------------------------------
 * Runs the test server side
 * Returns the app return code EXIT_FAILURE or EXIT_SUCCESS
 */
static int runServer(struct CommonArgs *commonArgs, struct ServerArgs *serverArgs) {
  SOCKET sock;
  struct sockaddr_in saddr_in;
  char *writeBuffer = NULL;
  SOCKET cliSock;
  struct sockaddr_in clientAddr;
  int i;
  int toRead;

  socklen_t addrlen = sizeof(clientAddr);
  int retVal = EXIT_FAILURE;
  // Client Parameters
  unsigned int clientParams[3] = {0, 0, 0};
  unsigned int clientBlockSize;
  unsigned int clientSendCount;
  unsigned int clientRecvCount;
  unsigned int clientAction;       // 1=continue, 0=stop
  int loopCount;

  struct timeval tWriteStart,
                 tWriteEnd,
                 tReadStart,
                 tReadEnd;

  if (isVerbose) {
    printf("Running SERVER with arguments:\n");
    printf("  Bind to address : %s\n", (serverArgs->bindAddress ? serverArgs->bindAddress : "ANY"));
    printf("  Server bind port: %d\n", serverArgs->port);
    printf("  Socket send buf.: %d (default=%d)\n", commonArgs->sendBufSize, theDefaultSendBufferSize);
    printf("  Socket recv buf.: %d (default=%d)\n", commonArgs->recvBufSize, theDefaultRecvBufferSize);
    printf("  TCP_NODELAY ....: %d (default=%d)\n", commonArgs->noNagle, theDefaultNagle);
  }

  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0) {
    fprintf(stderr, "Error creating socket: %d\n", tcpperfGetLastError());
    goto done;
  }

  // Set here all the various TCP settings
  if (!setSocketBufferSize(sock, commonArgs->sendBufSize, commonArgs->recvBufSize)) {
    fprintf(stderr, "Failed to set custom socket size\n");
    goto done;
  }
  if (commonArgs->noNagle) {
      disableNagle(sock);
  }

  // Bind server socket
  tcpperfZeroMemory(&saddr_in, sizeof(saddr_in) );
  saddr_in.sin_family = AF_INET;
  saddr_in.sin_port = htons((unsigned short)(serverArgs->port));
  if (serverArgs->bindAddress) {
    saddr_in.sin_addr.s_addr = htonl(inet_addr(serverArgs->bindAddress));
  } else {
    saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
  }
  i = bind(sock, (struct sockaddr *)&saddr_in, sizeof(saddr_in));
  if (i < 0) {
    fprintf(stderr, "Bind failed: %d\n", tcpperfGetLastError());
    goto done;
  }

  // Listen
  i = listen(sock, 10);
  if (i < 0) {
    fprintf(stderr, "Listen failed: %d\n", tcpperfGetLastError());
    goto done;
  }

  // Accept... wait for client connection
  cliSock = accept(sock, (struct sockaddr *)&clientAddr, &addrlen);
  if (cliSock < 0) {
    fprintf(stderr, "Accept failed: %d\n", tcpperfGetLastError());
    goto done;
  }
  if (isVerbose) {
    printf("Connected client from: %s:%d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
  }

  tcpperfCloseSocket(sock);

  if (isVerbose) printf("Getting running parameters from server...\n");
  // Now wait for the client to send 3 long words (using big endian):
  // - blockSize
  // - number of blocks to send
  // - number of blocks to receive
  if (recv(cliSock, (char *)&clientParams[0], sizeof(clientParams), 0) != sizeof(clientParams)) {
    fprintf(stderr, "Read client parameters error: %d\n", tcpperfGetLastError());
    goto done;
  }
  clientBlockSize = ntohl(clientParams[0]);
  clientSendCount = ntohl(clientParams[1]);
  clientRecvCount = ntohl(clientParams[2]);
  if (isVerbose) {
      printf("    blockSize: %d\n", clientBlockSize);
      printf("    SERVER sendCount: %d\n", clientSendCount);
      printf("    SERVER recvCount: %d\n", clientRecvCount);
  }
  
  // Create write buffer
  writeBuffer = (char *)malloc(clientBlockSize);
  for (i = 0; i < (int)clientBlockSize; ++i) {
    writeBuffer[i] = (i & 0xff);
  }

  for (loopCount = 0;;++loopCount) {
    printf("Loop #%d\n", loopCount+1);

    // SEND data to client phase
    if (clientSendCount > 0) {
      if (isVerbose) {
        printf("Waiting 1 second before sending %d blocks of data\n", clientSendCount);
      }
      tcpperfSleep(1);
      gettimeofday(&tWriteStart, NULL);
      for (i = 0; i < (int)clientSendCount; ++i) {
        retVal = send(cliSock, writeBuffer, clientBlockSize, 0);
        if (retVal < 0) {
          fprintf(stderr, "\nSocket write error: %d. Block#=%d\n", tcpperfGetLastError(), i);
          goto done;
        }
        if (isVerbose) {
          printf(".");
          fflush(stdout);
        }
      }
      gettimeofday(&tWriteEnd, NULL);
      
      if (isVerbose) {
        printf(" Done!\n");
      }
    } else {
      if (isVerbose) {
        printf("No data to send phase... Skipping to recv phase\n");
      }
    }

    // Recv phase
    if (clientRecvCount > 0) {
      if (isVerbose) {
        printf("Waiting for incoming data...\n");
      }

      retVal = recv(cliSock, writeBuffer, 1, 0);
      if (retVal == 0) {
        fprintf(stderr, "Client closed the connection\n");
        goto done;
      }
      if (retVal < 0) {
        fprintf(stderr, "recv failed: %d\n", tcpperfGetLastError());
        goto done;
      }
      gettimeofday(&tReadStart, NULL);
      toRead = clientBlockSize-1;

      for (i = 0; i < (int)clientRecvCount; ++i) {
        // Read one block
        while(toRead > 0) {
          retVal = recv(cliSock, writeBuffer, toRead, 0);
          if (retVal == 0) {
            fprintf(stderr, "\nClient closed the connection\n");
            goto done;
          }
          if (retVal < 0) {
            fprintf(stderr, "\nRecv failed: %d\n", tcpperfGetLastError());
            goto done;
          }
          toRead -= retVal;
        }
        if (isVerbose) { 
          printf("."); 
          fflush(stdout);
        }
        toRead = clientBlockSize;
      }
      gettimeofday(&tReadEnd, NULL);
      if (isVerbose) {
        printf("Recv phase completed\n");
      }
    } else {
      if (isVerbose) {
        printf("No recv phase...\n");
      }
    }

    printResults(loopCount, 
               clientBlockSize, 
               clientSendCount,
               clientRecvCount,
               &tWriteStart, 
               &tWriteEnd, 
               &tReadStart, 
               &tReadEnd);
    // now wait for the client to tell us what to do. Wait for a long. Value 
    // is 0 -> STOP
    // is 1 -> LOOP
    if (isVerbose) printf("Waiting for client command...\n");
    if (recv(cliSock, (char *)&clientParams[0], sizeof(clientParams[0]), 0) != sizeof(clientParams[0])) {
      fprintf(stderr, "Read wait command error: %d\n", tcpperfGetLastError());
      goto done;
    }
  
    clientAction = ntohl(clientParams[0]);
    if (isVerbose) printf("    Client command is: %d\n", clientAction);
    if (clientAction == 0) {
      break;
    }
    if (clientAction != 1) {
      fprintf(stderr, "Unexpected client command: %08x\n", clientAction);
      goto done;
    }
  }

  if (isVerbose) {
    printf("Client completed the tests, shutting down...\n");
  }
  tcpperfSleep(1);
  tcpperfCloseSocket(cliSock);

  retVal = EXIT_SUCCESS;
done:
  if (writeBuffer) {
    free(writeBuffer);
  }
  return retVal;
}
/* }}} */
/* {{{ runClient
 * --------------------------------------------------------------------------
 * Runs the test server side
 */
static int runClient(struct CommonArgs *commonArgs, struct ClientArgs *clientArgs) {
  SOCKET sock;
  struct addrinfo *result = NULL,
                  *ptr,
                   hints;
  int i, loopCount;
  int retVal = EXIT_FAILURE;
  char *writeBuffer = NULL;
  struct timeval tWriteStart,
                 tWriteEnd,
                 tReadStart,
                 tReadEnd;
  char portStr[10];
  unsigned int clientParam[3] = {0, 0, 0};
  int toRead;

  if (isVerbose) {
    printf("Running CLIENT with arguments:\n");
    printf("  Server remote address .: %s\n", (clientArgs->serverAddress ? clientArgs->serverAddress : "ANY"));
    printf("  Server remote port ....: %d\n", clientArgs->port);
    printf("  Block size ............: %d\n", clientArgs->blockSize);
    printf("  # of blocks to SEND ...: %d\n", clientArgs->sendCount);
    printf("  # of blocks to RECEIVE : %d\n", clientArgs->recvCount);
    printf("  Loop count ............: %d\n", clientArgs->loopCount);
    printf("  Socket send buf.: %d (default=%d)\n", commonArgs->sendBufSize, theDefaultSendBufferSize);
    printf("  Socket recv buf.: %d (default=%d)\n", commonArgs->recvBufSize, theDefaultRecvBufferSize);
    printf("  TCP_NODELAY ....: %d (default=%d)\n", commonArgs->noNagle, theDefaultNagle);
  }


  // Connect to remote server
  tcpperfZeroMemory( &hints, sizeof(hints) );
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = IPPROTO_TCP;
  tcpperfSnprintf(portStr, 10, "%d", clientArgs->port);

  i = getaddrinfo(clientArgs->serverAddress, portStr, &hints, &result);
  if (i) {
    fprintf(stderr, "getaddrinfo failed %d: %s\n", i, gai_strerror(i));
    goto done;
  }

  for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) {
    // Create a SOCKET for connecting to server
    sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
    if (sock == INVALID_SOCKET) {
        fprintf(stderr, "socket failed with error: %d\n", tcpperfGetLastError());
        goto done;
    }

    // Set here all the various TCP settings
    if (!setSocketBufferSize(sock, commonArgs->sendBufSize, commonArgs->recvBufSize)) {
      fprintf(stderr, "Failed to set custom socket size\n");
      goto done;
    }
    if (commonArgs->noNagle) {
      disableNagle(sock);
    }

    // Connect to server.
    i = connect(sock, ptr->ai_addr, (int)ptr->ai_addrlen);
    if (i == SOCKET_ERROR) {
        tcpperfCloseSocket(sock);
        sock = INVALID_SOCKET;
        continue;
    }
    break;
  }

  freeaddrinfo(result);

  if (sock == INVALID_SOCKET) {
     fprintf(stderr, "Unable to connect to server!\n");
     goto done;
  }
  

  // Create read/write buffer
  writeBuffer = calloc(clientArgs->blockSize, 1);
 
  if (isVerbose) {
    printf("Connected to remote server!\n");
  }

  // Send param to remote server
  clientParam[0] = htonl(clientArgs->blockSize);
  clientParam[1] = htonl(clientArgs->recvCount);  // Client's receive count are server's send count
  clientParam[2] = htonl(clientArgs->sendCount);  // and vice-versa
  if (send(sock, (char *)&clientParam[0], sizeof(clientParam), 0) != sizeof(clientParam)) {
    printf("\nError sending data to remote server: %d\n", tcpperfGetLastError());
    goto done;
  }

  for (loopCount = 0; loopCount < clientArgs->loopCount; ++loopCount) {
    if (isVerbose) printf("Loop #%d\n", loopCount);
    // RECEIVE
    if (clientArgs->recvCount > 0) {
      if (isVerbose) {
        printf("Ready to receive %d blocks from server...\n", clientArgs->recvCount);
      }
      retVal = recv(sock, writeBuffer, 1, 0);
      if (retVal == 0) {
        printf("Remote peer has closed the connection\n");
        goto done;
      }
      if (retVal < 0) {
        printf("socket read error: %d\n", tcpperfGetLastError());
      }
      gettimeofday(&tReadStart, NULL);
      toRead = clientArgs->blockSize-1;
      for (i = 0; i < clientArgs->recvCount; ++i) {

        // Read one block)
        while(toRead > 0) {
          retVal = recv(sock, writeBuffer, toRead, 0);
          if (retVal == 0) {
            printf("\nRemote peer has closed the connection\n");
            goto done;
          }
          if (retVal < 0) {
            printf("\nSocket read error: %d\n", tcpperfGetLastError());
            goto done;
          }
          toRead -= retVal;
        }
        if (isVerbose) {
          printf(".");
          fflush(stdout);
        }
        toRead = clientArgs->blockSize;
      }
      gettimeofday(&tReadEnd, NULL);
      if (isVerbose) {
        printf(" Done.\n");
      }
    }

    // SEND
    if (clientArgs->sendCount > 0) {
      for (i = 0; i < clientArgs->blockSize; ++i) {
        writeBuffer[i] = (i & 0xff);
      }

      if (isVerbose) printf("Sleeping 1 sec before sending data...\n");
      tcpperfSleep(1);
      gettimeofday(&tWriteStart, NULL);
      for (i = 0; i < clientArgs->sendCount; ++i) {
        retVal = send(sock, writeBuffer, clientArgs->blockSize, 0);
        if (retVal < 0) {
          printf("\nError sending data to remote server: %d\n", tcpperfGetLastError());
          goto done;
        }
        if (isVerbose) {
          printf(".");
          fflush(stdout);
        }
      }
      gettimeofday(&tWriteEnd, NULL);
      printf(" Done!\n");
    } else {
      if (isVerbose) {
        printf("No send phase...\n");
      }
    }
    
    printResults(loopCount,
                 clientArgs->blockSize, 
                 clientArgs->sendCount,
                 clientArgs->recvCount,
                 &tWriteStart, 
                 &tWriteEnd, 
                 &tReadStart, 
                 &tReadEnd);


    if (clientArgs->loopCount > 1) {
      if (clientArgs->loopDelay == 0) {
        printf("PRESS ENTER TO CONTINUE\n");
        getchar();
      } else {
        tcpperfSleep(clientArgs->loopDelay);
      }
      clientParam[0] = htonl(1); // Continue
    } else {
      clientParam[0] = 0;
    }

    // Send ok/stop to server
    if (send(sock, (char *)&clientParam[0], sizeof(clientParam[0]), 0) != sizeof(clientParam[0])) {
      printf("\nError sending OK command remote server: %d\n", tcpperfGetLastError());
      goto done;
    }
    --clientArgs->loopCount;
  }

  if (isVerbose) {
    printf("Shutting down...\n");
  }
  tcpperfSleep(1);
  tcpperfCloseSocket(sock);

  retVal = EXIT_SUCCESS;

done:
  if (writeBuffer) {
    free(writeBuffer);
  }
  return retVal;


}/* }}} */
/* {{{ banner
 * --------------------------------------------------------------------------
 */
void banner() {
  printf("TCP Performance Test version %s\n", APPLICATION_VERSION_STRING);
  printf("Copyright 2012 Broadway Technology, Inc.\n");
}
/* }}} */
/* {{{ usage
 * --------------------------------------------------------------------------
 */
void usage() {
  printf("Application can work in two modes: Server or Client\n");
  printf("Server mode: %s server [arguments]\n", APPLICATION_NAME);
  printf("  -p <portNum>      bind to port number (default: %d)\n", DEFAULT_PORT_NUMBER);
  printf("  -a <ip_address>   bind to given address (default: ANY)\n");
  printf("Client mode: %s client [arguments]\n", APPLICATION_NAME);
  printf("  -p <portNum>      server listen port number (default: 21500)\n");
  printf("  -a <address>      server remote address (default: localhost)\n");
  printf("  -r <num>          sets number of blocks to receive from server (default: %d)\n", DEFAULT_BLOCK_RECV);
  printf("  -s <num>          sets number of blocks to send to server (default: %d)\n", DEFAULT_BLOCK_SEND);
  printf("  -b <size>         sets block size (default: %d)\n", DEFAULT_BLOCK_SIZE);
  printf("Common arguments:\n");
  printf("  -q                quiet mode (print only results)\n");
  printf("  -h                show this page\n");
  printf("  -or <size>        set socket receive buffer size (default: %d)\n", theDefaultRecvBufferSize);
  printf("  -os <size>        set socket send buffer size (default: %d)\n", theDefaultSendBufferSize);
  printf("  -lc <count>       repeat send/receive pattern (default: 1)\n");
  printf("  -ld <delay>       delay between repetitions (default: 1). 0=ask\n");
  printf("  -nodelay          disable nagle algorithm (TCP_NODELAY)\n");
}
/* }}} */
/* {{{ main
 * --------------------------------------------------------------------------
 */

// Few convenience macros
#define ENSURE_ARGUMENT(i, argc, argv, arg)  \
      if (i >= argc-1) {  \
        printf("Missing argument for %s parameter\n", arg);   \
        usage();                                              \
        return EXIT_FAILURE;                                  \
      }

// Ensure the given value is ! < 0
#define ENSURE_NOT_NEGATIVE(val, arg) \
      if (val < 0) {                                                                  \
        fprintf(stderr, "Error: invalid value for argument '%s': %d\n", arg, val);    \
        usage();                                                                      \
        goto done;                                                                    \
      }

// Ensure the given value is > 0
#define ENSURE_POSITIVE(val, arg) \
      if (val <= 0) {                                                                  \
        fprintf(stderr, "Error: invalid value for argument '%s': %d\n", arg, val);    \
        usage();                                                                      \
        goto done;                                                                    \
      }


int main(int argc, char **argv) {
  int appReturnCode = EXIT_FAILURE;
  struct ServerArgs serverArgs = DEFAULT_SERVER_ARGS;
  struct ClientArgs clientArgs = DEFAULT_CLIENT_ARGS;
  struct CommonArgs commonArgs = DEFAULT_COMMON_ARGS;
  int    modeServer = 0;
  int i;

  banner();
  
  if (!initGlobals()) {
    goto done;
  }
  commonArgs.recvBufSize = theDefaultRecvBufferSize;
  commonArgs.sendBufSize = theDefaultSendBufferSize;

  if (argc < 2) {
    fprintf(stderr, "Error: missing mode 'client' or 'server'\n");
    usage();
    goto done;
  }

  if (!tcpperfStrcasecmp(argv[1], "server")) {
    modeServer = 1;
  } else if (tcpperfStrcasecmp(argv[1], "client")) {
    fprintf(stderr, "Error: invalid mode '%s'. Valid modes are 'server' or 'client'\n", argv[1]);
    usage();
    goto done;
  }
  

  for (i = 2; i < argc; ++i) {
    // Common Arguments
    if (!tcpperfStrcasecmp(argv[i], "-q")) {
      isVerbose = 0;
      continue;
    }

    if (!tcpperfStrcasecmp(argv[i], "-h")) {
      usage();
      appReturnCode = EXIT_SUCCESS;
      goto done;
    }

    if (!tcpperfStrcasecmp(argv[i], "-or")) {
      ENSURE_ARGUMENT(i, argc, argv, "-or");
      commonArgs.recvBufSize = atoi(argv[++i]);
      ENSURE_POSITIVE(commonArgs.recvBufSize, "-os");
      continue;
    }
    if (!tcpperfStrcasecmp(argv[i], "-os")) {
      ENSURE_ARGUMENT(i, argc, argv, "-os");
      commonArgs.sendBufSize = atoi(argv[++i]);
      ENSURE_POSITIVE(commonArgs.sendBufSize, "-os");
      continue;
    }
    if (!tcpperfStrcasecmp(argv[i], "-nodelay")) {
      commonArgs.noNagle = 1;
      continue;
    }

    // Client & Server Arguments
    if (!tcpperfStrcasecmp(argv[i], "-p")) {
      int portNumber;
      ENSURE_ARGUMENT(i, argc, argv, "-p");
      portNumber = atoi(argv[++i]);
      ENSURE_POSITIVE(portNumber, "-p");
      if (modeServer) {
        serverArgs.port = (unsigned short)portNumber;
      } else {
        clientArgs.port = (unsigned short)portNumber;
      }
      continue;
    }

    if (!tcpperfStrcasecmp(argv[i], "-a")) {
      ENSURE_ARGUMENT(i, argc, argv, "-a");
      ++i;
      if (modeServer) {
        serverArgs.bindAddress = argv[i];
      } else {
        clientArgs.serverAddress = argv[i];
      }
      continue;
    }

    if (!tcpperfStrcasecmp(argv[i], "-b")) {
      ENSURE_ARGUMENT(i, argc, argv, "-b");
      clientArgs.blockSize = atoi(argv[++i]);
      ENSURE_POSITIVE(clientArgs.blockSize, "-b");
      continue;
    }

    if (!tcpperfStrcasecmp(argv[i], "-s")) {
      ENSURE_ARGUMENT(i, argc, argv, "-s");
      clientArgs.sendCount = atoi(argv[++i]);
      ENSURE_NOT_NEGATIVE(clientArgs.sendCount, "-s");
      continue;
    }

    if (!tcpperfStrcasecmp(argv[i], "-r")) {
      ENSURE_ARGUMENT(i, argc, argv, "-r");
      clientArgs.recvCount = atoi(argv[++i]);
      ENSURE_NOT_NEGATIVE(clientArgs.recvCount, "-r");
      continue;
    }

    if (!tcpperfStrcasecmp(argv[i], "-lc")) {
      ENSURE_ARGUMENT(i, argc, argv, "-lc");
      clientArgs.loopCount = atoi(argv[++i]);
      ENSURE_POSITIVE(clientArgs.loopCount, "-lc");
      continue;
    } 

    if (!tcpperfStrcasecmp(argv[i], "-ld")) {
      ENSURE_ARGUMENT(i, argc, argv, "-ld");
      clientArgs.loopDelay = atoi(argv[++i]);
      ENSURE_NOT_NEGATIVE(clientArgs.loopDelay, "-l");
      continue;
    }

    fprintf(stderr, "Error: unknown argument: %s\n", argv[i]);
    goto done;
  }

  if (modeServer) {
    appReturnCode = runServer(&commonArgs, &serverArgs);
  } else {
    appReturnCode = runClient(&commonArgs, &clientArgs);
  }

done:
  shutdownGlobals();

  return appReturnCode;
}
/* }}} */


